﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Windows.Input;
using Foundation;
using Foundation.ExtensionMethods;
using Poet.Views;

namespace Poet.ViewModels
{
    /// <summary>
    /// Document Settings View Model.
    /// </summary>
    /// <typeparam name="TSet">The type of the sets.</typeparam>
    [DataContract]
    internal class DocumentSettingsViewModel<TSet> : ViewModel where TSet : class
    {
        private readonly string[] _ignoredProperties = new[] {"IsFrozen", "Dispatcher", "IsSealed"};

        /// <summary>
        /// Initializes a new instance of the <see cref="DocumentSettingsViewModel&lt;TSet&gt;"/> class.
        /// </summary>
        public DocumentSettingsViewModel()
        {
            Initialize();
        }

        /// <summary>
        /// Gets or sets the set as default.
        /// </summary>
        /// <value>
        /// The set as default.
        /// </value>
        public ICommand SetAsDefault { get; protected set; }

        /// <summary>
        /// Gets or sets the set default.
        /// </summary>
        /// <value>
        /// The set default.
        /// </value>
        public ICommand SetDefault { get; protected set; }

        /// <summary>
        /// Gets or sets the set factory.
        /// </summary>
        /// <value>
        /// The set factory.
        /// </value>
        public ICommand SetFactory { get; protected set; }

        /// <summary>
        /// Gets or sets the apply to all.
        /// </summary>
        /// <value>
        /// The apply to all.
        /// </value>
        public ICommand ApplyToAll { get; protected set; }

        /// <summary>
        /// Gets or sets the default.
        /// </summary>
        /// <value>
        /// The default.
        /// </value>
        [DataMember]
        public TSet DefaultSet { get; set; }

        /// <summary>
        /// Gets or sets the current set.
        /// </summary>
        /// <value>
        /// The current set.
        /// </value>
        public TSet CurrentSet
        {
            get { return Get(() => CurrentSet); }
            set { Set(() => CurrentSet, value); }
        }

        /// <summary>
        /// Gets the sets.
        /// </summary>
        [DataMember]
        public List<TSet> Sets { get; private set; }

        /// <summary>
        /// Gets the view model.
        /// </summary>
        public DocumentViewModel ViewModel
        {
            get
            {
                var documentView = Store.OfType<TrunkViewModel>().ActiveDocumentView as DocumentView;
                return documentView == null ? DocumentViewModel.Default : documentView.DataContext as DocumentViewModel;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public Action<DocumentViewModel, TSet> SetSettingsAction;

        /// <summary>
        /// 
        /// </summary>
        public Func<DocumentViewModel, TSet> GetSettingsAction;

        /// <summary>
        /// Gets the factory settings.
        /// </summary>
        /// <returns></returns>
        protected virtual TSet GetFactorySettings()
        {
            throw new NotImplementedException();
        }


        private void Executed()
        {    
            this[SetDefault].Executed += (o, eventArgs) => SetSettingsAction(ViewModel, DefaultSet);

            this[SetAsDefault].Executed += (o, eventArgs) =>
                {
                    DefaultSet = GetSettingsAction(ViewModel);
                    SetSettingsAction(DocumentViewModel.Default, DefaultSet);
                };

            this[SetFactory].Executed += (o, eventArgs) =>
                {
                    var factorySettings = GetFactorySettings();
                    SetSettingsAction(ViewModel, factorySettings);
                };

            this[ApplyToAll].Executed += (o, eventArgs) =>
                {
                    var documentViewModels =
                        Store.OfType<TrunkViewModel>()
                             .DocumentViews.Select(dv => dv.DataContext)
                             .OfType<DocumentViewModel>()
                             .ToList();

                    foreach (var documentViewModel in documentViewModels)
                        SetSettingsAction(documentViewModel, GetSettingsAction(ViewModel));
                };
        }

        private void CanExecute()
        {
            this[SetDefault].CanExecute += (o, args) =>
                {
                    var currentSettings = GetSettingsAction(ViewModel);
                    args.CanExecute = !Compare(currentSettings, DefaultSet);
                };

            this[SetAsDefault].CanExecute += (o, args) =>
                {
                    var currentSettings = GetSettingsAction(ViewModel);
                    args.CanExecute = !Compare(currentSettings, DefaultSet);
                };

            this[SetFactory].CanExecute += (o, args) =>
                {
                    var currentSettings = GetSettingsAction(ViewModel);
                    var factorySettings = GetFactorySettings();
                    args.CanExecute = !Compare(currentSettings, factorySettings);
                };

            this[ApplyToAll].CanExecute += (o, args) =>
                {
                    var documentViewModels =
                        Store.OfType<TrunkViewModel>()
                             .DocumentViews.Select(dv => dv.DataContext)
                             .OfType<DocumentViewModel>()
                             .ToList();

                    var currentSettings = GetSettingsAction(ViewModel);
                    args.CanExecute = documentViewModels.Any(vm => !Compare(GetSettingsAction(vm), currentSettings));
                };
        }

        private bool Compare(object x, object y)
        {
            return x.EqualsByProperties(y, 3, _ignoredProperties);
        }

        [OnDeserialized]
        private void Initialize(StreamingContext context = default(StreamingContext))
        {
            ApplyToAll = new RoutedUICommand();
            SetDefault = new RoutedUICommand();
            SetAsDefault = new RoutedUICommand();
            SetFactory = new RoutedUICommand();
            Executed();
            CanExecute();
            Store.OfType<TrunkViewModel>().PropertyChanged +=
                (o, e) =>
                    {
                        if (e.PropertyName == "ActiveDocumentView")
                            RaisePropertyChangedEvent(() => ViewModel);
                    };

            this[() => CurrentSet].PropertyChanged += (sender, args) => SetSettingsAction(ViewModel, CurrentSet);

            DefaultSet = DefaultSet ?? GetFactorySettings();
            Sets = Sets ?? new List<TSet> {DefaultSet};
        }
    }
}
